<?php

namespace Desktopd\MUA;


// SMIME (S/MIME)
class SMIME {
    protected $peerCerts;
    protected $caStore;
    protected $p12Key;
    
    
    public function __construct (PeerCertStore $peerCerts, CAStore $caStore, PKCS12 $p12Key = null) {
        $this->peerCerts = $peerCerts;
        $this->caStore = $caStore;
        $this->p12Key = $p12Key;
        
        
    }
    
    // returns ReceivedMessage
    // XXX: does NOT preserve headers!
    // So set headers AFTER encryption!
    public function encrypt (NewMessage $message, EmailAddress $recipient) {
        $cert = $this->peerCerts->get($recipient);
        if (!$cert) {
            throw new \RuntimeException('Recipient certificate not found');
        }
        
        $input = new TemporaryFile();
        $input->write($message->exportRaw());
        $output = new TemporaryFile();
        $result = openssl_pkcs7_encrypt(
            $input->getPath(),
            $output->getPath(),
            $cert,
            array(),
            0,
            OPENSSL_CIPHER_AES_256_CBC // not extremely good
        );
        
        
        $encrypted = $output->read();
        unset($input, $output);
        
        if (!$result) return false;
        $message->importRaw($encrypted);
        return $message;
    }
    
    public function sign (NewMessage $message) {
        $input = new TemporaryFile();
        $input->write($message->exportRaw());
        $output = new TemporaryFile();
        $result = openssl_pkcs7_sign(
            $input->getPath(),
            $output->getPath(),
            $this->p12Key->getCert(),
            $this->p12Key->getPrivateKey(),
            array() // headers
        );
        
        $signed = $output->read();
        unset($input, $output);
        
        if (!$result) return false;
        $message->importRaw($signed);
        return $message;
    }
    
    // returns ReceivedMessage
    public function decrypt (ReceivedMessage $message) {
        //openssl_pkcs7_decrypt
        $input = new TemporaryFile();
        $input->write("$message");
        $output = new TemporaryFile();
        $result = openssl_pkcs7_decrypt(
            $input->getPath(),
            $output->getPath(),
            $this->p12Key->getCert(),
            $this->p12Key->getPrivateKey()
        );
        
        
        $decrypted = $output->read();
        unset($input, $output);
        
        if (!$result) {
            while ($error = openssl_error_string()) {
                echo "$error\r\n";
            }
            var_dump($decrypted);
            return false;
        }
        return new ReceivedMessage($decrypted);
    }
    
    // returns ReceivedMessage
    public function verify (ReceivedMessage $message) {
        //openssl_pkcs7_verify();
        $input = new TemporaryFile();
        $input->write("$message");
        
        $output = new TemporaryFile(); // ideally /dev/null
        
        $caFiles = array();
        $caFileObjects = array(); // store references to managing objects
        foreach ($this->caStore->getAll() as $caData) {
            $caFile = new TemporaryFile();
            $caFile->write($caData);
            $caFiles[] = $caFile->getPath();
            $caFileObjects[] = $caFile;
            unset($caFile);
        }
        
        $content = new TemporaryFile();
        $result = openssl_pkcs7_verify(
            $input->getPath(),
            0,
            $output->getPath(),
            $caFiles,
            $caFiles[0],
            $content->getPath()
        );
        
        unset($input, $output, $caFileObjects);
        
        if ($result !== true) {
            return false;
        }
        
        $content = file_get_contents($content->getPath());
        
        return new ReceivedMessage($content); // verified contents
    }
    
    public function isSigned (ReceivedMessage $message) {
        $header = $message->getHeader();
        if (preg_match('~Content-[Tt]ype:\s+multipart/signed;(?:\s+.+?;)*\s+protocol=\"application/(?:x-)?pkcs7-signature\"~', $header)) {
            return true;
        }
        
        return false;
    }
    
    public function isEncrypted (ReceivedMessage $message) {
        $header = $message->getHeader();
        if (preg_match('~Content-[Tt]ype:\s+application/(?:x-)?pkcs7-mime;(?:\s+.+?;)*\s+smime-type=enveloped-data~', $header)) {
            return true;
        }
        
        return false;
    }
}


// vim: ts=4 et ai

